﻿#include <reg51.h>

#define KEY_OK           10
#define KEY_SET          11
#define KEY_NONE         0x10
#define ALARM_COUNT      7
#define TIMER_COUNT      10
#define BLINK_COUNT      6
#define DELAY_MS         125
#define DELAY_LED_CLEAR  50

#define STAT_TIMER        0
#define STAT_CLOCK_SET    1
#define STAT_ALARM_SET    2
#define STAT_ALARM_ING    3

// global variables
sbit P1_0 = 0x90;
unsigned char _timer0_count = 0x00;
unsigned char _timer1_count = 0x00;
code unsigned char _code[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//0,1...
code unsigned char _shift[6]={0x01,0x02,0x04,0x08,0x10,0x20}; //right to left
code unsigned char _key_shift[4]={0x1f,0x2f,0x37,0x3b};
code unsigned char _keyboard[4][4] ={   {0x07,0x08,0x09,0x0a},
                                        {0x04,0x05,0x06,0x0b},
                                        {0x01,0x02,0x03,0x0c},
                                        {0x00,0x0f,0x0e,0x0d}, };
xdata unsigned char _segment    _at_    0x8004;
xdata unsigned char _shift_code _at_    0x8002;
xdata unsigned char _key_code   _at_    0x8001;
unsigned char _key_;
unsigned char _status_ = STAT_TIMER;

struct Clock{
    unsigned char first; //from left to right
    unsigned char second;
    unsigned char third;
    unsigned char fourth;
    unsigned char fifth;
    unsigned char sixth;
    unsigned char dealed;
}system_clock, alarm_list[ALARM_COUNT];
char current_alarm = -1;
char alarm_ptr = -1;


// function declaration
unsigned char Helper_is_digit(unsigned char key);
void Chest_main_alarming();
void Chest_main_clocksetting();
void Chest_main_alarmsetting();
void Chest_main_timer();
void LED_display(unsigned char position, unsigned char digit);
void LED_clear();
void Notifier_alarm_noise();
void Notifier_alarm_stop();
void step_clock(struct Clock * system_clock);

void Notifier_alarm_noise(){
    P1_0 = 1;
}
void Notifier_alarm_stop(){
    P1_0 = 0;
}
void Helper_clear_deals(){
    unsigned char i;
    for(i=0;i<ALARM_COUNT;i++){
        alarm_list[i].dealed = 0;
    }
}

void Timer_intt0(void) interrupt 1{
    _timer0_count ++;
    if(_timer0_count == TIMER_COUNT){
        _timer0_count = 0x00;
        step_clock(&system_clock);
        Helper_clear_deals();
    }
    TH0 = 0x3c;
    TL0 = 0xB0;
}
//the interval should be smaller than 1 second.
// to call LED_clear()
void Timer_intt1(void) interrupt 3{
    _timer1_count ++;
    if(_timer1_count == BLINK_COUNT){
        _timer1_count = 0x00;
        LED_clear();
    }
    TH1 = 0x3c;
    TL1 = 0xB0;
}

void Timer_t0_start(){
    TH0 = 0x3c;
    TL0 = 0xB0;
    TR0 = 1;
    ET0 = 1;
}

void Timer_t1_start(){
    TH1 = 0x3c;
    TL1 = 0xB0;
    TR1 = 1;
    ET1 = 1;
}

void Timer_t1_stop(){
    ET1 = 0;
}

void Clock_display(struct Clock * self){
    LED_display(0,self->first);
    LED_display(1,self->second);
    LED_display(2,self->third);
    LED_display(3,self->fourth);
    LED_display(4,self->fifth);
    LED_display(5,self->sixth);
}

void Alarm_restore(struct Clock * self){
    self->sixth = self->fifth;
}

unsigned char Alarm_check_if_real_timeup(struct Clock * self){
    return self->sixth == 0;
}

void Alarm_deal_timeup_once(struct Clock * self){
    self->sixth --;
}

void Alarm_left_rotate(struct Clock * p, unsigned char key){
    if(! Helper_is_digit(key)) return;
    p->first = p->second;
    p->second = p->third;
    p->third = p->fourth;
    p->fourth = p->fifth;
    p->fifth = p->sixth;
    p->sixth = key;
}

void Clock_left_rotate(struct Clock * p, unsigned char key){
    if(! Helper_is_digit(key)) return;
    p->first = p->second;
    p->second = p->third;
    p->third = p->fourth;
    p->fourth = key;
}
void step_clock(struct Clock * system_clock){
    if(system_clock->sixth < 9){
        system_clock->sixth += 1;
        return;
    }
    system_clock->sixth = 0;

    if(system_clock->fifth < 5){
        system_clock->fifth += 1;
        return;
    }
    system_clock->fifth = 0;

    if(system_clock->fourth < 9){
        system_clock->fourth += 1;
        return;
    }
    system_clock->fourth = 0;

    if(system_clock->third < 5){
        system_clock->third += 1;
        return;
    }
    system_clock->third = 0;

    if(system_clock->first < 2){
        if(system_clock->second < 9){
            system_clock->second += 1;
            return;
        } else {
            system_clock->second = 0;
            system_clock->first += 1;
            return;
        }
    }

    if(system_clock->second < 3){
        system_clock->second += 1;
        return;
    }
    system_clock->second = 0;
    system_clock->first = 0;
}

unsigned char Alarm_validate(struct Clock * self){
    return self->third<=5 && (self->first*10+self->second)<24 ;
}

unsigned char Clock_validate(struct Clock * self){
    return self->fifth<=5 && Alarm_validate(self);
}

unsigned char Alarm_timeup(struct Clock * self){
    return self->first == system_clock.first &&
           self->second == system_clock.second &&
           self->third == system_clock.third &&
           self->fourth == system_clock.fourth &&
           system_clock.fifth ==0 &&
           system_clock.sixth == 0;
}

unsigned char Alarm_activated(struct Clock * self){
    return ! (self->first==0 && self->second==0 && self->third==0 &&
           self->fourth==0 && self->fifth==0 && self->sixth==0);
}

void Clock_initialize(struct Clock * ret){
    ret->first = ret->second = ret->third = ret->fourth = ret->fifth = ret->sixth=0;
    ret->dealed = 0;
}

void Helper_delay_ms(unsigned int ms){
    unsigned char i;
    while(ms--){
        for(i=DELAY_MS;i>0;i--);
    }
}

void Helper_beep_beep(unsigned char var1, unsigned var2){
    while(var1--){
        P1_0 = 1;
        Helper_delay_ms(var2);
        P1_0 = 0;
        Helper_delay_ms(var2);
    }
}

void Helper_beep_notify(){
}

void Helper_beep_error(){
    Helper_beep_beep(50,1);
}

void Helper_beep_fatal(){
    Helper_beep_beep(500,1);
}

//LED: singleton in system, <code>position</code> is from the left to right
// start at 0.
void LED_display(unsigned char position, unsigned char digit){
    _shift_code = 0x00;
    _segment = _code[digit];
    _shift_code = _shift[5-position];
    Helper_delay_ms(1);
}

void LED_clear(){
    unsigned char i;
    for(i=0;i<6;i++){
        _shift_code = 0x00;
    }
    Helper_delay_ms(DELAY_LED_CLEAR);
}

unsigned char Helper_is_digit(unsigned char key){
    return key<=9 && key>=0;
}


void Chest_main_timer(){
    Timer_t1_stop();
    Clock_display(&system_clock);
    for(alarm_ptr=0;alarm_ptr<ALARM_COUNT;alarm_ptr++){
        if(Alarm_activated(&alarm_list[alarm_ptr]) && 
                ( Alarm_timeup(&alarm_list[alarm_ptr]) )&&
                    (alarm_list[alarm_ptr].dealed == 0)){
            alarm_list[alarm_ptr].dealed =1;
            if(Alarm_check_if_real_timeup(&alarm_list[alarm_ptr])){
                _status_ = STAT_ALARM_ING;
                current_alarm = alarm_ptr;
                return;
            }else{
                Alarm_deal_timeup_once(&alarm_list[alarm_ptr]);
            }
        }
    }      

    if(_key_ == KEY_SET){
        _status_ = STAT_CLOCK_SET;
        Timer_t1_start();
        return;
    }else if(Helper_is_digit(_key_)){
        Helper_beep_error();
        return;
    }
}

void Chest_main_clocksetting(){
    Clock_display(&system_clock);

    current_alarm = -1;
    if(Helper_is_digit(_key_)){
        Clock_left_rotate(&system_clock,_key_);
        return;
    }else if(_key_ == KEY_OK){
        if(Clock_validate(&system_clock)){
            _status_ = STAT_TIMER;
            return;
        }else{
            Helper_beep_error();
            return;
        }
    }else if(_key_ == KEY_SET){
        if(Clock_validate(&system_clock)){
            _status_= STAT_ALARM_SET;
            current_alarm = 0;
            return;
        }else{
            Helper_beep_error();
            return;
        }
    }
}

void Chest_main_alarmsetting(){
    if(current_alarm == -1){
        Helper_beep_fatal();
        return;
    }
    Clock_display(&alarm_list[current_alarm]);

    if(Helper_is_digit(_key_)){
        Alarm_left_rotate(&alarm_list[current_alarm],_key_);
        return;
    }else if(_key_ == KEY_OK){
        if(Alarm_validate(&alarm_list[current_alarm])){
            _status_ = STAT_TIMER;
            current_alarm = -1;
            return;
        }else{
            Helper_beep_error();
            return;
        }
    }else if(_key_ == KEY_SET){
        if(! Alarm_validate(&alarm_list[current_alarm])){
            Helper_beep_error();
            return;
        }
        if(current_alarm == ALARM_COUNT-1){
            _status_ = STAT_TIMER;
            return;
        }
        current_alarm ++;
    }
}

void Chest_main_alarming(){
    Notifier_alarm_noise();
    Clock_display(&alarm_list[current_alarm]);

    if(Helper_is_digit(_key_) || _key_ == KEY_OK || _key_ == KEY_SET){
        _status_ = STAT_TIMER;
        Alarm_restore(&alarm_list[current_alarm]);
        current_alarm = -1;
        Notifier_alarm_stop();
        return;
    }
}

void Scanner_scan(){
    unsigned char i;
    unsigned char row;
    for(i=0;i<4;i++){
        _shift_code = _key_shift[i];
        if(_key_code!=0x0f){
            _shift_code = 0x00;
            Helper_delay_ms(1);
            _shift_code = _key_shift[i];
            if(_key_code!=0x0f){
                if(_key_code==0x0e)
                    row = 3;
                else if(_key_code==0x0d)
                    row = 2;
                else if(_key_code==0x0b)
                    row = 1;
                else if(_key_code==0x07)
                    row = 0;
                _key_ = _keyboard[row][i];
                _shift_code = 0x00;
                while(_key_code != 0x0f);
                return;
            }
        }
    }
    _key_ = KEY_NONE;
}

void Timer_initialize(){
    TMOD = 0x11;
    EA = 1;
    Timer_t0_start();
}

void Chest_main_init_ports(){
    Notifier_alarm_stop();
}

void Chest_main_initialize(){
    char i;
    Timer_initialize();
    Chest_main_init_ports();
    Clock_initialize(&system_clock);
    for(i=0;i<ALARM_COUNT;i++){
        Clock_initialize(&alarm_list[i]);
    }
}

void main(void){
    Chest_main_initialize();
    while(1){
        _key_ = KEY_NONE;
        Scanner_scan();
        switch(_status_){
            case STAT_TIMER:
                Chest_main_timer();
                break;
            case STAT_CLOCK_SET:
                Chest_main_clocksetting();
                break;
            case STAT_ALARM_SET:
                Chest_main_alarmsetting();
                break;
            case STAT_ALARM_ING:
                Chest_main_alarming();
                break;
        }
    }
}
